import {Matrix} from "@/components/comm/matrix";
import {HexContainer, HexPosition} from "@/components/hex/HexContainer";
import {Position} from "@/components/comm/utils";

export enum HexMatrixType {
    ODD_R = 0,  // 奇数偏移，尖朝上
    ODD_Q = 1,  // 奇数偏移，边朝上
    EVEN_R = 3, // 偶数偏移，尖朝上
    EVEN_Q = 4, // 偶数偏移，边朝上
}

export class HexMatrix<T> extends HexContainer<T> {
    public readonly data: Matrix<T>;

    public constructor(
        readonly rows: number,
        readonly cols: number,
        readonly tp: HexMatrixType,
        fn: (pos: HexPosition) => T
    ) {
        super();
        this.data = Matrix.from(rows, cols, index => {
            const pos = this.indexToCube(index);
            return fn(pos);
        });
    }

    private cubeToOddR([x, _, z]: HexPosition): Position {
        return [z, x + (z - (z & 1)) / 2];
    }

    private oddRToCube([i, j]: Position): HexPosition {
        const x = j - (i - (i & 1)) / 2;
        const z = i;
        const y = -x - z;
        return [x, y, z];
    }

    private cubeToOddQ([x, _, z]: HexPosition): Position {
        return [z + (x - (x & 1)) / 2, x];
    }

    private oddQToCube([i, j]: Position): HexPosition {
        const x = j;
        const z = i - (j - (j & 1)) / 2;
        const y = -x - z;
        return [x, y, z];
    }

    private cubeToEvenR([x, _, z]: HexPosition): Position {
        return [z, x + (z + (z & 1)) / 2];
    }

    private evenRToCube([i, j]: Position): HexPosition {
        const x = j - (i + (i & 1)) / 2;
        const z = i;
        const y = -x - z;
        return [x, y, z];
    }

    private cubeToEvenQ([x, _, z]: HexPosition): Position {
        return [z + (x + (x & 1)) / 2, x];
    }

    private evenQToCube([i, j]: Position): HexPosition {
        const x = j;
        const z = i - (j + (j & 1)) / 2;
        const y = -x - z;
        return [x, y, z];
    }

    public cubeToIndex(pos: HexPosition): Position {
        switch (this.tp) {
            case HexMatrixType.ODD_R:
                return this.cubeToOddR(pos);
            case HexMatrixType.ODD_Q:
                return this.cubeToOddQ(pos);
            case HexMatrixType.EVEN_R:
                return this.cubeToEvenR(pos);
            case HexMatrixType.EVEN_Q:
                return this.cubeToEvenQ(pos);
        }
    }

    public indexToCube(index: Position): HexPosition {
        switch (this.tp) {
            case HexMatrixType.ODD_R:
                return this.oddRToCube(index);
            case HexMatrixType.ODD_Q:
                return this.oddQToCube(index);
            case HexMatrixType.EVEN_R:
                return this.evenRToCube(index);
            case HexMatrixType.EVEN_Q:
                return this.evenQToCube(index);
        }
    }

    public contains(pos: HexPosition): boolean {
        const index = this.cubeToIndex(pos);
        return this.data.contains(index);
    }

    public* indexes(): Generator<HexPosition> {
        for (const index of this.data.indexes()) {
            yield this.indexToCube(index);
        }
    }
}